﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Classes
{
    class GraphImpl : IGraphImpl
    {
        IList<City> cities;
        IList<Flight> flights;
        IList<City> citiesOfSpan;
        IList<Flight> flightsForSearch;
        IList<City> sequenceOfParentCities;
        IList<Flight> listOfFlights;
        IList<Flight> sequence;
        IList<City> theCitiesList;
         
        
        City theCityWeGoFrom;
        City theCityWeGoTo;



        IList<City> citiesInConnection;
        IList<Flight> flightsInConnection;







        ListOfConnections listOfConnections;
        Connection connection;



        public GraphImpl()
        {
            citiesOfSpan = new List<City>();
            flights = new List<Flight>();
            cities = new List<City>();
            flightsForSearch = new List<Flight>();
            connection = new Connection();
            sequenceOfParentCities = new List<City>();
            listOfFlights = new List<Flight>();
            sequence = new List<Flight>(); // GetARoutetoTheCity(City from, City to)
            theCitiesList = new List<City>(); //  public IList<City> GetCities(City city)

            citiesInConnection = new List<City>();
            flightsInConnection = new List<Flight>();





            connection = new Connection();
            listOfConnections = new ListOfConnections();
        }
        public void SetTheCityFrom(City v)
        {
            theCityWeGoFrom = v;
        }
        public void SetTheCityTo(City v)
        {
            theCityWeGoTo= v;

        }
        public IList<City> ReturnCityList()
        {
            return cities;
        }
        public bool IsEmpty()
        {
            return cities.Count == 0;
        }
        public Connection ReturnConnection()
        {
            return connection;
        }




        public void AddSpanTreeToTheConnection(IList<Flight> spanTreeFlights)
        {
            

            foreach (Flight f in spanTreeFlights)
            {
                connection.AddFlight(f);
            }

        }
        public void PurifyConnection(City to)
        {

        }
        /*    public IList<Flight> SearchForConnection(City from, City to)
            {
                from.Mark = true;
                ICollection<City> adj = GetAdjacencies(from);
                foreach (City c in adj)
                {
                    flightsForSearch.Add(new Flight(from, c));
                    if (c != to)
                    {
                        if (c.Mark != true)
                        {
                            SearchForConnection(c, to);
                        }
                    }

                }
          
            }
            */
        public void AddCity(City c)
        {
            cities.Add(c);
        }
        public void AddFlight(City from, City to)
        {	//PRE (to,from) is not contained in the graph

            from.AddFlight(to);
        }

        public bool ContainsCity(City city)
        {
            return cities.Contains(city);
        }

        public bool IsAdjacent(City startCity, City endCity)
        {
            bool isAdjacent = false;
            foreach (Flight f in startCity.Flights())
            {
                if (f.To == endCity)
                {
                    isAdjacent = true;
                }
            }
            return isAdjacent;

        }

        public IList<City> GetAdjacencies(City city)
        {
            IList<City> list = new List<City>();
            foreach (Flight f in city.Flights())
            {
                list.Add(f.To);
            }
            return list;

        }


        /* I don't know how to implement this shit and when to use it
         public IEnumerator<City> Cities()
         {
             throw new System.NotImplementedException();
         }
         */

        public int GetNoOfCities()
        {
            int i = cities.Count;
            return i;
        }

        public int GetNoOfFlights()
        {
            int numberOfFlights = 0;
            foreach (City c in this.cities)
            {

                numberOfFlights += c.Flights().Count;
            }
            return numberOfFlights;
        }
        public void Unmark()
        {
            for (int i = 0; i < cities.Count; i++)
                (cities[i]).Mark = false;
        }
        public void Bfs(City v)
        {
            Queue<City> q = new Queue<City>();
            v.Mark = true;
            q.Enqueue(v);
            int level = 0;
            while (q.Count != 0)
            {
                City w = q.Dequeue();
                Console.WriteLine("***********************************");
                Console.WriteLine(w.Name);
                Console.WriteLine("***********************************");

                if (w.Mark)
                {
                    level++;
                }

                Console.WriteLine("Level: " + level);
                IList<City> adj = GetAdjacencies(w);
                for (int i = 0; i < adj.Count; i++)
                {
                    City u = adj[i];
                    Console.WriteLine(u.Name);
                    if (!u.Mark)
                    {
                        u.Mark = true;
                        q.Enqueue(u);
                    }
                }
            }
        }
        public IList<Flight> SpanTreeOfFlightsInBfs(City v)
        {
            Queue<City> q = new Queue<City>();
            v.Mark = true;
            q.Enqueue(v);
            int level = 0;
            while (q.Count != 0)
            {
               City w = q.Dequeue();
               if (w.Mark)
               {
                   level++;
               }
                IList<City> adj = GetAdjacencies(w);
                for (int i = 0; i < adj.Count; i++)
                {
                    City u = adj[i];
                    if (!u.Mark)
                    {
                        Flight flight = new Flight(w, u);
                        flights.Add(flight);
                        Console.WriteLine(flight.From.Name + " ----> " + flight.To.Name);
                        u.Mark = true;
                        q.Enqueue(u);
                    }
                }
            }
            return flights;

        }

        public ListOfConnections BreadthFirst(IList<City> visited)
        {
            IList<City> adj = GetAdjacencies(visited.Last());
            foreach(City c in adj)
            {
                if (visited.Contains(c))
                {
                    continue;
                }
                if (c.Equals(theCityWeGoTo))
                {
                    visited.Add(c);
                    IList<Flight> flightsForConnection = connection.MakeFlightsOfCities(visited);
                    connection.SetFlightList(flightsForConnection);
                    listOfConnections.AddConnection(connection);
                 //   PrintPath(visited);
                    City f = visited.Last();
                    visited.Remove(f);
                   // break; now it does actually work work
                }
               foreach(City x in adj)            
               if (visited.Contains(x) || x.Equals(theCityWeGoTo)) {
                continue;
            }
        
            visited.Add(c);
            BreadthFirst(visited);
            City last = visited.Last();
            visited.Remove(last);
        }
            return listOfConnections;
      }
        
        public void PrintPath(IList<City> visited)
        {
        foreach(City c in visited)
        {
            Console.WriteLine(c.Name);
            Console.WriteLine(" ");
        }
        Console.WriteLine();
        }


        public void Clear()
        {
            cities = new List<City>();
        }

        /*      public void Bfs(City v)
              {
                  throw new System.NotImplementedException();

              }*/

        public void Dfs(City v)//normal version
        {
            v.Mark = true;
            Console.WriteLine(v.Name + " " + v.Country);
            ICollection<City> adj = GetAdjacencies(v);
            foreach (City u in adj)
            {
                if (u.Mark != true)
                {
                    Dfs(u);
                }
            }
        }
        public IList<City> SpanTreeCities(City city)
        {

            city.Mark = true;
            citiesInConnection.Add(city);
            ICollection<City> adj = GetAdjacencies(city);
            foreach (City u in adj)
            {
                if (u.Mark != true)
                {
                    SpanTreeCities(u);
                }
            }
           
            return citiesInConnection;

        }





        public IList<Flight> GetSpanTree(City city)
        {
            city.Mark = true;
            ICollection<City> adj = GetAdjacencies(city);
            foreach (City u in adj)
            {
                if (u.Mark != true)
                {
                    Flight flight = new Flight(city, u);
                    flightsInConnection.Add(flight);
                    GetSpanTree(u);
                }
            }

            return flightsInConnection;

        }




        public IList<City> GetParentsOfThisCity(City city)
        {
            IList<City> listOfCities = new List<City>();
            foreach (City c in cities)
            {
                foreach (City i in GetAdjacencies(c))
                {
                    if (i == city)
                    {
                        listOfCities.Add(c);
                    }
                }

            }

            return listOfCities;
        }




        public IList<City> MakeASequenceOfCitiesFromASequenceOfCities(IList<City> cityList)//we add here a list of Cities from graph
        {
            IList<City> sequence = new List<City>();

            foreach (City c in cityList)
            {

                Console.WriteLine("The paretns of city " + c.Name + " are:");
                GetParentsOfThisCity(c);

                foreach (City f in GetParentsOfThisCity(c))
                {
                    Console.WriteLine(f.Name);

                    sequence.Add(f);
                }
                Console.ReadLine();

            }
            return sequence;
        }





    }
}


/*
   public void BreadthFirst(IList<City> visited)
        {
            IList<City> adj = GetAdjacencies(visited.Last());
            foreach(City c in adj)
            {
                if (visited.Contains(c))
                {
                    continue;
                }
                if (c.Equals(theCityWeGoTo))
                {
                    visited.Add(c);
                    PrintPath(visited);
                    City f = visited.Last();
                    visited.Remove(f);
                   // break; now it does actually work work
                }
               foreach(City x in adj)            
               if (visited.Contains(x) || x.Equals(theCityWeGoTo)) {
                continue;
            }
            
            visited.Add(c);
            BreadthFirst(visited);
            City last = visited.Last();
            visited.Remove(last);
        }
      }
*/